/*
 * @Author: Suo-Yuheng syh15399227325@163.com
 * @Date: 2025-09-08 18:10:02
 * @LastEditors: Suo-Yuheng syh15399227325@163.com
 * @LastEditTime: 2025-09-09 11:35:01
 * @FilePath: /0907/tcp/TcpServer.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "task.hpp"
#include "Daemon.hpp"

using namespace std;

const int defaultfd = -1;
const string defaultip = "0.0.0.0";
const int backlog = 5; // 一般不要设置的太大

log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};

class TcpServer;

struct ThreadData
{
    ThreadData(int fd, const string &ip, const uint16_t &port, TcpServer *t)
        :_sockfd(fd), clientip(ip), clientport(port), tsvr(t)
    {}

    int _sockfd;
    string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port, const string &ip = defaultip)
        : _listen_sockfd(defaultfd), _port(port), _ip(ip)
    {
    }

    void InitServer()
    {
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建 流式套接字
        if (_listen_sockfd < 0)
        {
            lg(Fatal, "create socker, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, _listen_sockfd: %d", _listen_sockfd);


        int opt = 1;
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行 立即重启(tcp协议会讲)
        

        struct sockaddr_in local; // 本地套接字信息
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // 主机转网络

        // inet_aton(_ip.c_str(), &(local.sin_addr)); // 主机转网络
        local.sin_addr.s_addr = INADDR_ANY; // 任意绑定

        socklen_t len = sizeof(local);
        int n = bind(_listen_sockfd, (struct sockaddr *)&local, len); // 绑定 套接字和对应的文件描述符
        if (n < 0)
        {
            lg(Fatal, "bind failed, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind socket success, _listen_sockfd: %d", _listen_sockfd);

        // Tcp 是面向 连接的，服务器一般是 比较 被动的，服务器一直在等待 连接 到来的 状态 —— 监听状态
        if (listen(_listen_sockfd, backlog) < 0)
        {
            lg(Fatal, "listen failed, errno: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen socket success, _listen_sockfd: %d", _listen_sockfd);
    }


    static void *Routine(void *args)    // 避免成员函数的 this 指针
    {
        pthread_detach(pthread_self()); // 设置自己线程为 分离状态,无需等待 join
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tsvr->Service(td->_sockfd, td->clientip, td->clientport);   // 此时用 this 指针调用成员方法
        delete td;
        return nullptr;
    }

    void Start()
    {
        Daemon();   // 父进程调用的,调用完成之后,下面的代码已经是由 子进程执行了

        // signal(SIGPIPE, SIG_IGN);   // 防止写入时 读/写 端有一端已经断开连接了,再写直接受到 pipe 信号,进程被杀掉

        ThreadPool<Task> *tp = new ThreadPool<Task>();
        tp->Start();


        // signal(SIGCHLD, SIG_IGN);   // 这里忽略掉 子进程的 等待信号,父进程就不用去显式 等待了
        
        lg(Info, "TcpServer is running...");

        for (;;)
        {
            // 1、获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)&client, &len); // 又是一个 文件描述符？
            if (sockfd < 0)
            {
                lg(Warning, "accept failed, errno: %d, errstring: %s", errno, strerror(errno)); // ??
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2、根据新连接来进行 通信
            lg(Info, "get a new link... , sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);


            // version 1  单进程版本
            // Service(sockfd, clientip, clientport);
            // close(sockfd);


            // version 2 --  多进程版本
            // pid_t id = fork();
            // if (id == 0)    // 子进程会继承 文件描述符表
            // {
            //     close(_listen_sockfd);  // 不需要用,关掉

            //     if(fork() > 0)  exit(0);    // 这样 waitpid 就不会被阻塞了!!!

            //     Service(sockfd, clientip, clientport);  // 这样,孙子进程就去执行任务了,子进程直接挂掉,孙子进程被 system 领养,自动回收
            //     close(sockfd);
            //     exit(0);
            // }

            // close(sockfd);  // 这个 sockfd 交给 子进程去服务了,父进程不需要了,关掉
            // pid_t rid = waitpid(id, nullptr, 0);    // 因为 子进程创建孙子进程后,自己就退出了,所以这里直接wait 成功,不会阻塞
            // (void)rid;


            // version 3 -- 多线程版本
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);    // 需要this 指针 以调用 service 成员方法
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);    // 想办法把 新的套接字 ip port  交给线程

            // // pthread_join(tid, nullptr);


            // version 4 -- 线程池版本
            Task t(sockfd, clientip, clientport);
            tp->Push(t);
        }
    }

    void Service(int sockfd, const string &clientip, const uint16_t &clientport)
    {
        char buffer[4096];
        while (true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if (n > 0)
            {
                buffer[n] = 0;
                cout << "client say# " << buffer << endl;
                string echo_string = "tcpserver echo# ";
                echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0) // 如果客户端退了,那么 read 的返回值 为 0
            {
                lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                // read 出错了,打印客户端信息
                lg(Info, "read error , sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }

    ~TcpServer()
    {
    }

private:
    int _listen_sockfd;
    uint16_t _port;
    string _ip;
};